package aor2.cpu.blockoperation;

import aor2.components.BitMasks;
import aor2.components.Register;
import aor2.components.Restorable;
import aor2.components.Signal;
import aor2.cpu.InternBus;
import aor2.cpu.blockcontrol.ConditionSignalSet;
import aor2.cpu.blockcontrol.ControlSignalSet;
import aor2.cpu.blockinterrupt.InterruptBlock;

/**
 * 
 * @author Moxi
 * 
 */

public class Psw extends Register implements Restorable {

	private static long PswState = PswBitMask.Z.getMask()
			| PswBitMask.I.getMask();
	// TODO psw - lupio

	private static Psw singleton = null;

	public static Psw getInstance() {
		if (singleton == null)
			singleton = new Psw(PswState);
		return singleton;
	}

	public enum PswBitMask {
		N(0x01), Z(0x02), C(0x04), V(0x08), L0(0x10), L1(0x20), T(0x4000), I(
				0x8000);

		private PswBitMask(long mask) {
			this.mask = mask;
		}

		public long getMask() {
			return mask;
		}

		private long mask;
	}

	private Psw(long startPswState) {
		super(startPswState, InternBus.M2, InternBus.M1);
		ControlSignalSet css = ControlSignalSet.getInstance();

		ldpsw = css.get("ldpsw");
		ldpsw.addDestinationElementIO(this);

		ldpswl = css.get("ldpswl");
		ldpswl.addDestinationElementIO(this);

		pswin = css.get("PSWin");
		pswin.addDestinationElementIO(this);
		css.get("PSWout").addSourceElementIO(this);

		clpswi = css.get("clpswi");
		clpswi.addDestinationElementIO(this);

		clpswt = css.get("clpswt");
		clpswt.addDestinationElementIO(this);

		stpswi = css.get("stpswi");
		stpswi.addDestinationElementIO(this);

		stpswt = css.get("stpswt");
		stpswt.addDestinationElementIO(this);
	}

	private Signal ldpsw, ldpswl, pswin, clpswi, clpswt, stpswi, stpswt;

	@Override
	public void in() {
		if (pswin.isActive())
			super.in();
		if (ldpsw.isActive())
			ldpsw();
		if (ldpswl.isActive())
			ldpswl();
		if (clpswi.isActive())
			clpswi();
		if (clpswt.isActive())
			clpswt();
		if (stpswi.isActive())
			stpswi();
		if (stpswt.isActive())
			stpswt();
	}

	private void ldpswl() {
		InterruptBlock ib = InterruptBlock.getInstance();
		if (ib.newL0())
			stpswl0();
		else
			clpswl0();
		if (ib.newL1())
			stpswl1();
		else
			clpswl1();
	}

	private void stpswt() {
		state |= PswBitMask.T.getMask();
	}

	private void clpswt() {
		state &= ~PswBitMask.T.getMask();
	}

	private void stpswi() {
		state |= PswBitMask.I.getMask();
	}

	private void clpswi() {
		state &= ~PswBitMask.T.getMask();
	}

	private void stpswn() {
		state |= PswBitMask.N.getMask();
	}

	private void clpswn() {
		state &= ~PswBitMask.N.getMask();
	}

	private void stpswz() {
		state |= PswBitMask.Z.getMask();
	}

	private void clpswz() {
		state &= ~PswBitMask.Z.getMask();
	}

	private void stpswv() {
		state |= PswBitMask.V.getMask();
	}

	private void clpswv() {
		state &= ~PswBitMask.V.getMask();
	}

	private void stpswl0() {
		state |= PswBitMask.L0.getMask();
	}

	private void clpswl0() {
		state &= ~PswBitMask.L0.getMask();
	}

	private void stpswl1() {
		state |= PswBitMask.L1.getMask();
	}

	private void clpswl1() {
		state &= ~PswBitMask.L1.getMask();
	}

	private void stpswc() {
		state |= PswBitMask.C.getMask();
	}

	private void clpswc() {
		state &= ~PswBitMask.C.getMask();
	}

	private void ldpsw() {
		AlushiftXYM1 alu = AlushiftXYM1.getInstance();

		long xstate = alu.x().getState();
		long ystate = alu.y().getState();
		long f = alu.getState();

		if ((f & BitMasks.bit15) != 0)
			stpswn();
		else
			clpswn();

		Signal eql = ConditionSignalSet.getInstance().get("eql");
		if ((f & 0xFFFF) == 0) {
			stpswz();
			eql.setActive(true);
		} else {
			clpswz();
			eql.setActive(false);
		}

		switch (alu.getOperation()) {
		case inc: {
			if ((f & BitMasks.bit16) != 0)// carry
				stpswc();
			else
				clpswc();

			if ((f & BitMasks.bit15) != 0 && (xstate & BitMasks.bit15) == 0)// valid
				stpswv();
			else
				clpswv();
			break;
		}
		case dec: {
			if ((f & BitMasks.bit16) == 0)// carry
				stpswc();
			else
				clpswc();
			if ((f & BitMasks.bit15) == 0 && (xstate & BitMasks.bit15) != 0)// valid
				stpswv();
			else
				clpswv();
			break;
		}
		case asr: {
			if ((xstate & BitMasks.bit1) != 0)// carry
				stpswc();
			else
				clpswc();
			clpswv();
			break;
		}
		case sub: {
			if ((f & BitMasks.bit16) == 0)// carry
				stpswc();
			else
				clpswc();

			if (((f & BitMasks.bit15) == 0 && (xstate & BitMasks.bit15) != 0 && (ystate & BitMasks.bit15) == 0)
					|| ((f & BitMasks.bit15) != 0
							&& (xstate & BitMasks.bit15) == 0 && (ystate & BitMasks.bit15) != 0))// valid
				stpswv();
			else
				clpswv();
			break;
		}
		default: {
			clpswv();
		}
		}
	}

	public boolean IC() {
		AlushiftXYM1 alu = AlushiftXYM1.getInstance();

		long xstate = alu.x().getState();
		long f = alu.getState();

		switch (alu.getOperation()) {
		case inc: {
			if ((f & BitMasks.bit16) != 0)// carry
				return true;
			else
				return false;
		}
		case dec: {
			if ((f & BitMasks.bit16) == 0)// carry
				return true;
			else
				return false;
		}
		case asr: {
			if ((xstate & BitMasks.bit1) != 0)// carry
				return true;
			else
				return false;
		}
		case sub: {
			if ((f & BitMasks.bit16) == 0)// carry
				return true;
			else
				return false;
		}
		default: {
			return bitC();
		}
		}
	}

	public boolean IZ() {
		AlushiftXYM1 alu = AlushiftXYM1.getInstance();
		long f = alu.getState();
		return (f & 0xFFFF) == 0;
	}

	public boolean IV() {
		AlushiftXYM1 alu = AlushiftXYM1.getInstance();

		long xstate = alu.x().getState();
		long ystate = alu.y().getState();
		long f = alu.getState();

		switch (alu.getOperation()) {
		case inc: {

			if ((f & BitMasks.bit15) != 0 && (xstate & BitMasks.bit15) == 0)// valid
				return true;
			else
				return false;
		}
		case dec: {
			if ((f & BitMasks.bit15) == 0 && (xstate & BitMasks.bit15) != 0)// valid
				return true;
			else
				return false;
		}
		case asr: {
			return false;
		}
		case sub: {
			if (((f & BitMasks.bit15) == 0 && (xstate & BitMasks.bit15) != 0 && (ystate & BitMasks.bit15) == 0)
					|| ((f & BitMasks.bit15) != 0
							&& (xstate & BitMasks.bit15) == 0 && (ystate & BitMasks.bit15) != 0))// valid
				return true;
			else
				return false;
		}
		default: {
			return false;
		}
		}
	}

	public boolean IN() {
		AlushiftXYM1 alu = AlushiftXYM1.getInstance();
		long f = alu.getState();
		return (f & BitMasks.bit15) != 0;
	}

	public boolean bitT() {
		return (state & PswBitMask.T.getMask()) != 0;
	}

	public boolean bitI() {
		return (state & PswBitMask.I.getMask()) != 0;
	}

	public boolean bitL1() {
		return (state & PswBitMask.L1.getMask()) != 0;
	}

	public boolean bitL0() {
		return (state & PswBitMask.L0.getMask()) != 0;
	}

	public boolean bitV() {
		return (state & PswBitMask.V.getMask()) != 0;
	}

	public boolean bitC() {
		return (state & PswBitMask.C.getMask()) != 0;
	}

	public boolean bitZ() {
		return (state & PswBitMask.Z.getMask()) != 0;
	}

	public boolean bitN() {
		return (state & PswBitMask.N.getMask()) != 0;
	}

	@Override
	public String toString() {
		return "PSW: " + state + " [N:" + bitN() + " Z:" + bitZ() + " C:"
				+ bitC() + " V:" + bitV() + " L0:" + bitL0() + " L1:" + bitL1()
				+ " T:" + bitT() + " I:" + bitI() + "]";
	}

	@Override
	public void restore(String restorePoint) {
		String trim = restorePoint.replace(beginTag(), "");
		trim = trim.replace(endTag(), "");
		trim = trim.trim();
		setState(Long.parseLong(trim));
	}

	@Override
	public String createRestorePoint() {
		return beginTag() + "\n\t" + state + "\n" + endTag();
	}

	@Override
	public String beginTag() {
		return "<PSW>";
	}

	@Override
	public String endTag() {
		return "</PSW>";
	}
}
